﻿namespace MicroCloud.Options
{
    /// <summary>
    /// 系统数据上下文配置选项
    /// </summary>
    public class DbContextOption : DataErrorInfoBase
    {
        #region 构造函数
        /// <summary>
        /// 初始化一个系统数据上下文配置选项 <see cref="DbContextOption"/> 的新实例
        /// </summary>
        public DbContextOption()
        {
            SlaveDatabase = new SlaveDatabaseOption { SlaveDatabases = Array.Empty<SlaveDatabaseInfo>() };

            IsolationLevel = null;
            CommandTimeout = null;
            LazyLoadingProxiesEnabled = true;
            DateTimeUtcFormatEnabled = false;
            EntityAuditEnabled = true;
            EntityEventEnabled = true;
            MigrationEnabled = false;
        }

        #endregion

        #region 属性
        /// <summary>
        /// 获取 上下文类型
        /// </summary>
        public Type DbContextType => string.IsNullOrEmpty(DbContextTypeName) ? null : Type.GetType(DbContextTypeName);

        /// <summary>
        /// 获取或设置 上下文类型全名
        /// </summary>
        [Required(AllowEmptyStrings = false, ErrorMessage = "上下文类型全名不能为空")]
        public string DbContextTypeName { get; set; }

        /// <summary>
        /// 获取或设置 数据库类型
        /// </summary>
        public DatabaseType DatabaseType { get; set; }

        /// <summary>
        /// 获取或设置 连接字符串
        /// </summary>
        [Required(AllowEmptyStrings = false, ErrorMessage = "数据库连接串不能为空")]
        public string ConnectionString { get; set; }

        /// <summary>
        /// 获取或设置 从数据库配置
        /// </summary>
        public SlaveDatabaseOption SlaveDatabase { get; set; }

        /// <summary>
        /// 获取或设置 命令执行超时时长(秒)
        /// </summary>
        public int? CommandTimeout { get; set; }

        /// <summary>
        /// 获取或设置 事务隔离级别
        /// <para>Unspecified：-1 - 正在使用与指定隔离级别不同的隔离级别，但是无法确定该级别。</para>
        /// <para>Chaos：16 - 无法覆盖隔离级别更高的事务中的挂起的更改。</para>
        /// <para>ReadUncommitted：256 - 可以进行脏读，意思是说，不发布共享锁，也不接受独占锁。</para>
        /// <para>ReadCommitted：4096 - 在正在读取数据时保持共享锁，以避免脏读，但是在事务结束之前可以更改数据，从而导致不可重复的读取或幻像数据。</para>
        /// <para>RepeatableRead：65536 - 在查询中使用的所有数据上放置锁，以防止其他用户更新这些数据。 防止不可重复的读取，但是仍可以有幻像行。</para>
        /// <para>Serializable：1048576 - 在 DataSet 上放置范围锁，以防止在事务完成之前由其他用户更新行或向数据集中插入行。</para>
        /// <para>Snapshot：16777216 - 通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改，即便重新查询也是如此。</para>
        /// </summary>
        public IsolationLevel? IsolationLevel { get; set; }

        /// <summary>
        /// 获取或设置 是否启用延迟加载代理
        /// </summary>
        public bool LazyLoadingProxiesEnabled { get; set; }

        /// <summary>
        /// 获取或设置 是否启用时间UTC格式
        /// </summary>
        public bool DateTimeUtcFormatEnabled { get; set; }

        /// <summary>
        /// 获取或设置 是否启用数据审计
        /// </summary>
        public bool EntityAuditEnabled { get; set; }

        /// <summary>
        /// 获取或设置 是否启用数据事件
        /// </summary>
        public bool EntityEventEnabled { get; set; }

        /// <summary>
        /// 获取或设置 迁移的程序集名称
        /// <para>为空时默认"DesignTimeXXXDbContextFactory"所在程序集</para>
        /// </summary>
        public string MigrationAssemblyName { get; set; }

        /// <summary>
        /// 获取或设置 启用自动迁移
        /// </summary>
        public bool MigrationEnabled { get; set; }

        /// <summary>获取 一条错误消息，指示此对象有什么问题。</summary>
        /// <returns>指示此对象存在什么问题的错误消息。默认值为空字符串（""）。</returns>
        public override string Error
        {
            get
            {
                string[] props = { "DbContextTypeName", "ConnectionString" };
                foreach (string prop in props)
                {
                    string msg = this[prop];
                    if (msg != string.Empty)
                    {
                        return I18N.T("属性 {0} 验证失败：{1}", prop, msg);
                    }
                }

                if (DbContextType == null)
                {
                    return I18N.T("属性 DbContextTypeName 提供的类型 {0} 不存在", DbContextTypeName);
                }

                if (SlaveDatabase != null && SlaveDatabase?.SlaveDatabases != null)
                {
                    foreach (var slaveDatabase in SlaveDatabase.SlaveDatabases)
                    {
                        string msg = slaveDatabase.Error;
                        if (msg != string.Empty)
                        {
                            return msg;
                        }
                    }
                }

                return string.Empty;
            }
        }

        #endregion

    }
}